using System;
using System.Xml;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;

namespace Microsoft.Samples.BizTalk.Adapter.Tcp
{
    /// <summary>
    ///     This class provides static methods that help with parsing the configuration XML from BizTalk.
    /// </summary>
    /// <author>
    ///     Paul Brown
    /// </author>
    public class ConfigurationHelper
    {
        #region Public Constants

        // The adapter property namespace
        public const string AdapterPropertyNamespace = "http://schemas.microsoft.com/BizTalk/samples/adapter/tcp";

        // The adapter properties
        public const string DataEncoding = "DataEncoding";
        public const string DataEncodingPreamble = "DataEncodingPreamble";
        public const string ListenerTerminateTimeout = "ListenerTerminateTimeout";
        public const string ReceiveDataBufferSize = "ReceiveDataBufferSize";
        public const string SendDataBufferSize = "SendDataBufferSize";
        public const string ResponseMessageTimeout = "ResponseMessageTimeout";
        public const string ListenPort = "ListenPort";
        public const string MaxPendingConnections = "MaxPendingConnections";
        public const string IsDuplex = "IsDuplex";
        public const string DuplexPollTimeout = "DuplexPollTimeout";
        public const string CloseTimeout = "CloseTimeout";
        public const string DuplexCloseTimeout = "DuplexCloseTimeout";
        public const string ClientCloseTimeout = "ClientCloseTimeout";
        public const string SocketReceiveBufferSize = "SocketReceiveBufferSize";
        public const string SocketSendBufferSize = "SocketSendBufferSize";
        public const string SocketReceiveTimeout = "SocketReceiveTimeout";
        public const string SocketSendTimeout = "SocketSendTimeout";
        public const string IsOrdered = "IsOrdered";
        public const string SuspendSubsequentMessages = "SuspendSubsequentMessages";
        public const string FrameStartDelimiter = "FrameStartDelimiter";
        public const string FrameEndDelimiter = "FrameEndDelimiter";
        public const string FrameEncoding = "FrameEncoding";
        public const string FrameEncodingPreamble = "FrameEncodingPreamble";
        public const string EndpointAddress = "EndpointAddress";
        public const string EndpointPort = "EndpointPort";
        public const string SupportsDuplex = "SupportsDuplex";
        public const string PartyEndpointName = "PartyEndpointName";

        #endregion

        #region Public Static Methods

        /// <summary>
        ///     This method loads the adapter configuration XML from the message context.
        /// </summary>
        public static XmlDocument LoadAdapterConfiguration(IAdapter adapter, IBaseMessage message)
        {
            if (adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.LoadingAdapterConfiguration);

            // Extract adapter configuration
            object propValue = message.Context.Read("AdapterConfig", ConfigurationHelper.AdapterPropertyNamespace);

            if (propValue == null)
            {
                return (null);
            }

            // Load configuration into the DOM
            XmlDocument configXml = new XmlDocument();
            configXml.LoadXml(propValue as string);
            return (configXml);
        }

        /// <summary>
        ///     This method extracts the adapter configuration XML from the property bag.
        /// </summary>
        public static XmlDocument LoadAdapterConfiguration(IAdapter adapter, IPropertyBag configuration)
        {
            if (adapter.TraceSwitch.TraceVerbose)
                TraceHelper.WriteEntry(TraceLevel.Verbose, ResourceConstants.LoadingAdapterConfiguration);

            // Extract adapter configuration (XML document as a property of the property bag)
            object propValue;
            configuration.Read("AdapterConfig", out propValue, 0);

            if (propValue == null)
            {
                return (null);
            }

            // Load configuration into the DOM
            XmlDocument configXml = new XmlDocument();
            configXml.LoadXml(propValue as string);
            return (configXml);
        }

        /// <summary>
        ///     This method gets an entry in the configuration XML and throws an error if it doesn't exist.
        /// </summary>
        public static XmlNode GetEntry(XmlDocument configuration, string xPath)
        {
            return (ConfigurationHelper.GetEntry(configuration, xPath, true));
        }

        /// <summary>
        ///     This method gets an entry in the configuration XML and either throws an error if it doesn't exist or
        ///     it returns null depending on the value of the throwIfNotFound flag.
        /// </summary>
        public static XmlNode GetEntry(XmlDocument configuration, string xPath, bool throwIfNotFound)
        {
            // Get node from configuration
            XmlNode node = configuration.SelectSingleNode(xPath);

            if (node == null)
            {
                if (throwIfNotFound)
                {
                    string message = TraceHelper.FormatEntry(ResourceConstants.ConfigurationPropertyMissing, xPath);
                    TraceHelper.WriteRawEntry(TraceLevel.Error, message);
                    throw new AdapterConfigurationException(message);
                }
            }

            // Return node
            return (node);
        }

        /// <summary>
        ///     This method returns the correct encoding based on the input parameters or null if binary encoding
        ///     is specified.
        /// </summary>
        public static Encoding GetEncoding(XmlNode encodingNode, string customEncodingValue, EndianTypes byteOrder, bool outputByteOrderMark)
        {
            Encoding encoding = null;
            DataEncodingTypes dataEncoding = (DataEncodingTypes)Enum.Parse(typeof(DataEncodingTypes), encodingNode.InnerText);
            switch (dataEncoding)
            {
                // ASCII
                case DataEncodingTypes.ASCII:

                    encoding = Encoding.ASCII;
                    break;

                // Binary
                case DataEncodingTypes.Binary:

                    encoding = null;
                    break;

                // Custom Identifier
                case DataEncodingTypes.CustomIdentifier:

                    encoding = Encoding.GetEncoding(int.Parse(customEncodingValue));
                    break;

                // Custom Name
                case DataEncodingTypes.CustomName:

                    encoding = Encoding.GetEncoding(customEncodingValue);
                    break;

                // Unicode UTF-16
                case DataEncodingTypes.UTF16:

                    if (byteOrder == EndianTypes.LittleEndian)
                    {
                        encoding = new UnicodeEncoding(false, outputByteOrderMark);
                    }
                    else
                    {
                        encoding = new UnicodeEncoding(true, outputByteOrderMark);
                    }
                    break;

                // Unicode UTF-32
                case DataEncodingTypes.UTF32:

                    if (byteOrder == EndianTypes.LittleEndian)
                    {
                        encoding = new UTF32Encoding(false, outputByteOrderMark, true);
                    }
                    else
                    {
                        encoding = new UTF32Encoding(true, outputByteOrderMark, true);
                    }
                    break;

                // UTF-7
                case DataEncodingTypes.UTF7:

                    encoding = Encoding.UTF7;
                    break;

                // UTF-8
                case DataEncodingTypes.UTF8:

                    encoding = new UTF8Encoding(outputByteOrderMark, true);
                    break;
            }

            return (encoding);
        }

        /// <summary>
        ///     This method returns a hex string in the form NN NN NN from the byte array.
        /// </summary>
        public static string GetHexString(byte[] bytes)
        {
            if (bytes.Length > 0)
            {
                StringBuilder sb = new StringBuilder(bytes.Length * 2);
                for (int i = 0; i < bytes.Length; i++)
                {
                    sb.AppendFormat("{0:X2} ", bytes[i]);
                }
                return (sb.ToString(0, sb.Length - 1));
            }
            else
            {
                return (string.Empty);
            }
        }

        #endregion
    }
}
